package checkers;

import java.util.ArrayList;
import java.util.Random;

/**
 * AI is a static library that allows us to generate an intelligent move
 * based on an instance of a game object. Relies on implementation scoreBoard()
 * found within the game class to evaluate how desirable a board is
 * 
 * @author Zachary Hullihen, Gary Raymond
 * 
 */
public class AI {

	public static final int MAX_DIFF = 100;

	/**
	 * Gets the desired move of the AI based on the board configuration and the difficulty.
	 * The difficulty must be between 1-100 with 100 being the hardest difficulty.
	 * <p>
	 * 
	 * @param g - Game
	 * @param difficulty  1-100
	 * @return The desired move.
	 */
	public static int[] getMove(Game g, int difficulty) {

		Random rNumber = new Random(System.nanoTime());

		// if not our turn, can't make a move
		if (g.currentPlayer() == false) {
			return null;
		}
		int[] bestMove = new int[4];
		int bestScore = Integer.MIN_VALUE;

		ArrayList<int[]> pastMoves = new ArrayList<int[]>();

		int si = 0;
		int sj = 0;
		int ei = 0;
		int ej = 0;

		Game temp = new Game(g);
		for (int i = 0; i < Game.BOARD_LENGTH; i++) {
			for (int j = 0; j < Game.BOARD_LENGTH; j++) {
				if (temp.hypSelect(i, j)) {
					si = i;
					sj = j;
					for (int k = 0; k < Game.BOARD_LENGTH; k++) {
						for (int l = 0; l < Game.BOARD_LENGTH; l++) {
							if (!(si == k && sj == l)) {
								if (temp.hypSelect(k, l)) {
									if (temp.selectedEmpty() == false) {
										temp.hypSelect(k, l);
									}
									ei = k;
									ej = l;
									int[] moveSet = { si, sj, ei, ej };
									if (isNewMove(pastMoves, moveSet)) {
										pastMoves.add(moveSet);
										System.out
										.printf("\npossible Move %d %d, %d %d\n",
												moveSet[0], moveSet[1],
												moveSet[2], moveSet[3]);
										int score = getLookAheadScore(temp);
										if (score > bestScore) {
											bestMove = moveSet;
											bestScore = score;
											System.out
											.printf("\nmove success %d %d, %d %d\n",
													moveSet[0],
													moveSet[1],
													moveSet[2],
													moveSet[3]);
											// restart outer loops
											// exit inner loops
										}
										i = 0;
										j = 0;
										k = Integer.MAX_VALUE - 1;
										l = Integer.MAX_VALUE - 1;
										temp = new Game(g);
									} else {
										System.out.println("duplicate move");
										temp = new Game(g);
										temp.select(i, j);

									}
								}
							}
						}
					}
				}
				temp = new Game(g);
			}
		}

		if (rNumber.nextInt(MAX_DIFF) <= difficulty) {
			return bestMove;
		} else {
			System.out.println("pastMoves size is " + pastMoves.size());
			return pastMoves.get(rNumber.nextInt(pastMoves.size()));
		}
	}

	/**
	 * Similar to getMove(), but instead makes moves for the human player
	 * used to look at the score of best possible move the human player can make
	 * 
	 * @param g reference to a game object
	 * @return int score of the best move a human player can make on a given board
	 */
	public static Integer getLookAheadScore(Game g) {

		if (g.currentPlayer() == true) {
			return null;
		}
		int bestScore = Integer.MIN_VALUE;

		ArrayList<int[]> pastMoves = new ArrayList<int[]>();

		int si = 0;
		int sj = 0;
		int ei = 0;
		int ej = 0;

		Game temp = new Game(g);
		for (int i = 0; i < Game.BOARD_LENGTH; i++) {
			for (int j = 0; j < Game.BOARD_LENGTH; j++) {
				if (temp.hypSelect(i, j)) {
					si = i;
					sj = j;
					for (int k = 0; k < Game.BOARD_LENGTH; k++) {
						for (int l = 0; l < Game.BOARD_LENGTH; l++) {
							if (!(si == k && sj == l)) {
								if (temp.hypSelect(k, l)) {
									if (temp.selectedEmpty() == false) {
										temp.hypSelect(k, l);
									}
									ei = k;
									ej = l;
									int[] moveSet = { si, sj, ei, ej };
									if (isNewMove(pastMoves, moveSet)) {
										pastMoves.add(moveSet);
										System.out
										.printf("\npossible Move %d %d, %d %d\n",
												moveSet[0], moveSet[1],
												moveSet[2], moveSet[3]);
										int score = temp.scoreBoard();
										if (score > bestScore) {
											bestScore = score;
											System.out
											.printf("\nmove success %d %d, %d %d\n",
													moveSet[0],
													moveSet[1],
													moveSet[2],
													moveSet[3]);
											// restart outer loops
											// exit inner loops
										}
										i = 0;
										j = 0;
										k = Integer.MAX_VALUE - 1;
										l = Integer.MAX_VALUE - 1;
										temp = new Game(g);
									} else {
										System.out.println("duplicate move");
										temp = new Game(g);
										temp.select(i, j);

									}
								}
							}
						}
					}
				}
				temp = new Game(g);
			}
		}
		return bestScore;
	}

	/**
	 * isNewMove takes in an arraylist of moves(a move consists of an int array) and
	 * a single move, and then returns true if that move was found in the set of past
	 * moves
	 * 
	 * @param pastMoves an arraylist of int arrays that represent moves already tried
	 * @param move int array of the move we are verifying is new
	 * @return true if a new move
	 */
	public static boolean isNewMove(ArrayList<int[]> pastMoves, int[] move) {
		for (int i = 0; i < pastMoves.size(); i++) {
			if (equalMove(pastMoves.get(i), move)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * equalMove is a method that takes in two integer arrays of any size
	 * and determines if those arrays are equal, moves are defined as a sequence
	 * of numbers where the first(index 0) represents the the row of the first
	 * select, and the second index(index 1) represents the column of the first select.
	 * The second select begins on the third index(index 2) and ect
	 * 
	 * @param m1 first move
	 * @param m2 second move
	 * @return true if equal
	 */
	public static boolean equalMove(int[] m1, int[] m2) {
		if (m1.length != m2.length) {
			return false;
		}

		for (int i = 0; i < m1.length; i++) {
			// System.out.println("m1[" + i + "]: " + m1[i]);
			// System.out.println("m2[" + i + "]: " + m2[i]);
			if (m1[i] != m2[i]) {
				return false;
			}
		}

		return true;
	}

}
